// 工具页面数据
export const toolsData = {
  // 工具分类
  categories: [
    {
      name: '基础业务',
      tools: [
        '可拖拽的table表格',
        '含操作列的table表格',
        '含过滤器的table表格',
        '含复杂模态框的table表格',
        '分页组件',
        '虚拟滚动',
        '地图模块',
        '时间线'
      ]
    }
  ],
  
  // 页面各部分数据
  sections: [
    {
      title: '可拖拽的table表格',
      content: '这是一个可拖拽的table表格组件，用户可以通过拖动列头来调整列的宽度。'
    },
    {
      title:'含操作列的table表格',
      content: `这是一个可以在表格每一行添加操作按钮的table表格组件，用户可以通过点击操作按钮来执行相应的操作。`
    },
    {
      title: '含过滤器的table表格',
      content: `这是一个可以通过表头下方的过滤行来实现对表格每列数据进行单独的筛选和过滤。`
    },
    {
      title: '含复杂模态框的table表格',
      content: `这是一个含复杂模态框的table表格，具体应用场景具体分析`    },
    {
      title: '分页组件',
      content: '解决用户快速点击，因网络通信延迟导致的分页错误问题，确保用户在分页切换时能够准确获取到对应的数据。<br>下方示例通过模拟控制台的输出和第二页第三页两个按钮，通过使用定时器模拟网络通信延迟，<br>当用户快速点击第二页或第三页按钮时，会发送对应的网络请求，当还没有返回数据时切换页码，会中断前一次请求。<br>在实际应用中需要使用abort来中断上一次网络请求。'
    },
    {
      title: '虚拟滚动',
      content: `这是一个虚拟滚动组件，用于在大数据量下实现流畅的滚动体验。`
    },
    {
      title: '地图模块',
      content: `这是一个地图模块，用于在页面上展示地图和相关的定位功能。`
    },
    {
      title: '时间线',
      content: `这是一个时间线组件，用于展示事件的发生顺序和时间点。`
    }
  ],
  
  // 组件映射关系
  componentMap: {
    '可拖拽的table表格': 'mobileTable',
    '含操作列的table表格': 'operationTable',
    '含过滤器的table表格': 'filterTable',
    '含复杂模态框的table表格': 'modalTable',
    '分页组件': 'pagination',
    '虚拟滚动': 'virtualScroll',
    '地图模块': 'mapModule',
    '时间线': 'timeline'
  },
  
  // 源代码映射关系
  sourceCodeMap: {
    '可拖拽的table表格': `<template>
  <div class="test-table" ref="tableContainer">
    
    <div class="table-header-wrapper">
      <table ref="headerTable" class="header-table">
        <colgroup>
          <col v-for="(width, index) in columnWidths" :key="index" :style="{ width: width + 'px' }" />
        </colgroup>
        <thead>
          <tr>
            <th v-for="(header, index) in headers" :key="header" class="resizable-header" :ref="el => { if (el) headerRefs[index] = el; }">
              <div class="header-content">{{ header }}</div>
              <div 
                v-if="index < headers.length - 1"
                class="resize-handle" 
                @mousedown="startResize($event, index)"
              ></div>
            </th>
          </tr>
        </thead>
      </table>
    </div>

    <div class="table-body-wrapper" ref="tableBodyWrapper" @scroll="onTableScroll">
      <table ref="bodyTable" class="body-table">
        <colgroup>
          <col v-for="(width, index) in columnWidths" :key="index" :style="{ width: width + 'px' }" />
        </colgroup>
        <tbody>
          <tr v-for="(row, rowIndex) in rows" :key="rowIndex">
            <td v-for="(cell, cellIndex) in row" :key="cellIndex">{{ cell }}</td>
          </tr>
        </tbody>
      </table>
    </div>

  </div>
</template>

<script>
export default {
  name: 'mobileTable',
  data() {
    return {
      headers: ['姓名', '年龄', '城市', '职业', '学历', '工作经验', '薪资', '部门', '入职日期', '状态'],
      rows: [
        ['张三', '25', '北京', '工程师', '本科', '3年', '15K', '技术部', '2020-01-15', '在职'],
        ['李四', '30', '上海', '设计师', '硕士', '5年', '20K', '设计部', '2019-03-22', '在职'],
        ['王五', '28', '广州', '产品经理', '本科', '4年', '18K', '产品部', '2020-07-10', '在职'],
        ['赵六', '32', '深圳', '运营', '本科', '6年', '16K', '运营部', '2018-11-05', '在职'],
        ['钱七', '27', '杭州', '数据分析师', '硕士', '2年', '14K', '数据部', '2021-02-18', '在职'],
        ['孙八', '29', '成都', '测试工程师', '本科', '4年', '15K', '技术部', '2020-05-30', '在职'],
        ['周九', '31', '武汉', 'UI设计师', '硕士', '6年', '19K', '设计部', '2018-09-12', '在职'],
        ['吴十', '26', '西安', '前端工程师', '本科', '2年', '13K', '技术部', '2021-08-25', '在职'],
        ['张三', '25', '北京', '工程师', '本科', '3年', '15K', '技术部', '2020-01-15', '在职'],
        ['李四', '30', '上海', '设计师', '硕士', '5年', '20K', '设计部', '2019-03-22', '在职'],
        ['王五', '28', '广州', '产品经理', '本科', '4年', '18K', '产品部', '2020-07-10', '在职'],
        ['赵六', '32', '深圳', '运营', '本科', '6年', '16K', '运营部', '2018-11-05', '在职'],
        ['钱七', '27', '杭州', '数据分析师', '硕士', '2年', '14K', '数据部', '2021-02-18', '在职'],
        ['孙八', '29', '成都', '测试工程师', '本科', '4年', '15K', '技术部', '2020-05-30', '在职'],
        ['周九', '31', '武汉', 'UI设计师', '硕士', '6年', '19K', '设计部', '2018-09-12', '在职'],
        ['吴十', '26', '西安', '前端工程师', '本科', '2年', '13K', '技术部', '2021-08-25', '在职']
      ],
      // 预定义每列的宽度
      columnWidths: [120, 80, 100, 120, 100, 120, 100, 120, 120, 100],
      headerRefs: [], // 存储表头元素的引用
      resizing: false, // 是否正在调整列宽
      startX: 0, // 开始拖拽时的X坐标
      startWidthLeft: 0, // 开始拖拽时左侧列的宽度
      startWidthRight: 0, // 开始拖拽时右侧列的宽度
      leftIndex: -1, // 左侧列的索引
      rightIndex: -1 // 右侧列的索引
    };
  },
  mounted() {
    document.addEventListener('mousemove', this.onMouseMove);
    document.addEventListener('mouseup', this.onMouseUp);
  },
  beforeUnmount() {
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('mouseup', this.onMouseUp);
  },
  methods: {
    startResize(event, index) {
      this.resizing = true;
      this.leftIndex = index;
      this.rightIndex = index + 1;
      this.startX = event.clientX;
      this.startWidthLeft = this.columnWidths[this.leftIndex];
      this.startWidthRight = this.columnWidths[this.rightIndex];
      
      event.preventDefault();
    },
    
    onMouseMove(event) {
      if (!this.resizing) return;
      
      document.body.classList.add('resizing');
      
      const diff = event.clientX - this.startX;
      
      const totalWidth = this.startWidthLeft + this.startWidthRight;
      let newWidthLeft = this.startWidthLeft + diff;
      let newWidthRight = this.startWidthRight - diff;
      
      if (newWidthLeft < 50) {
        newWidthLeft = 50;
        newWidthRight = totalWidth - 50;
      } else if (newWidthRight < 50) {
        newWidthRight = 50;
        newWidthLeft = totalWidth - 50;
      }
      
      this.columnWidths.splice(this.leftIndex, 1, newWidthLeft);
      this.columnWidths.splice(this.rightIndex, 1, newWidthRight);
    },
    
    onMouseUp() {
      if (this.resizing) {
        this.resizing = false;
        this.leftIndex = -1;
        this.rightIndex = -1;
        
        document.body.classList.remove('resizing');
      }
    },
    
    onTableScroll(event) {
      // 同步表头和表格数据的滚动位置
      if (this.$refs.headerTable) {
        this.$refs.headerTable.style.transform = \`translateX(\${-event.target.scrollLeft}px)\`;
      }
    }
  }
};
</script>

<style scoped>
.test-table {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.table-header-wrapper {
  overflow: hidden;
  flex-shrink: 0;
}

.header-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
  min-width: 1200px;
}

.table-body-wrapper {
  overflow-x: auto;
  overflow-y: scroll; /* 启用垂直滚动 */
  flex-grow: 1;
  margin-top: -1px; 
  max-height: 400px; 
  
  &::-webkit-scrollbar {
    width: 0px; /* 隐藏垂直滚动条 */
    height: 12px; /* 水平滚动条高度 */
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #2d2d2d;
    
    &:hover {
      background-color: #777;
    }
  }

  &::-webkit-scrollbar-corner {
    background-color: #2d2d2d;
  }
}

.body-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
  min-width: 1200px;
}

th,
td {
  padding: 12px 15px;
  text-align: left;
  border-bottom: 1px solid #444;
  border-right: 1px solid #444;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

th:last-child,
td:last-child {
  border-right: none;
}

th {
  color: white;
  font-weight: bold;
  position: relative;
  user-select: none;
  border-right: 1px solid #444;
}

tr:hover {
  background-color: #3a3a3a;
}

.resizable-header {
  padding: 0;
}

.header-content {
  padding: 12px 15px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resize-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}

body.resizing {
  cursor: col-resize;
  user-select: none;
}
</style>`,
    '含操作列的table表格':`<template>
  <div class="test-table" ref="tableContainer">
    <div class="mode-checkboxes">
      <label class="mode-checkbox">
        <input type="radio" v-model="currentMode" value="visitor" name="mode" />
        <span>游客模式</span>
      </label>
      <label class="mode-checkbox">
        <input type="radio" v-model="currentMode" value="admin" name="mode" />
        <span>管理员模式</span>
      </label>
    </div>

    <div class="table-header-wrapper">
      <table ref="headerTable" class="header-table">
        <colgroup>
          <col style="width: 40px" v-show="adminMode" />
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" v-show="header.key !== 'operation' || adminMode" />
        </colgroup>
        <thead>
          <tr>
            <th class="checkbox-header" @mouseenter="showHeaderCheckbox" @mouseleave="hideHeaderCheckbox" v-show="adminMode">
              <div class="custom-checkbox" v-show="showHeaderCheckboxes" @click="handleSelectAllChange">
                <span v-if="selectAll" class="checkbox-icon checked">✓</span>
                <span v-else-if="indeterminate" class="checkbox-icon indeterminate">─</span>
                <span v-else class="checkbox-icon unchecked">○</span>
              </div>
            </th>
            <th v-for="(header, index) in headers" :key="header.key" class="resizable-header" :ref="el => { if (el) headerRefs[index] = el; }" v-show="header.key !== 'operation' || adminMode">
              <div class="header-content">{{ header.label }}</div>
              <div 
                v-if="index < headers.length - 1 && header.key !== 'operation'"
                class="resize-handle" 
                @mousedown="startResize($event, index)"
              ></div>
            </th>
          </tr>
        </thead>
      </table>
    </div>

    <div class="table-body-wrapper" ref="tableBodyWrapper" @scroll="onTableScroll">
      <table ref="bodyTable" class="body-table">
        <colgroup>
          <col style="width: 40px" v-show="adminMode" />
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" v-show="header.key !== 'operation' || adminMode" />
        </colgroup>
        <tbody>
          <tr v-for="(row, rowIndex) in rows" :key="rowIndex" @mouseenter="showCheckbox(rowIndex)" @mouseleave="hideCheckbox(rowIndex)">
            <td class="checkbox-cell" v-show="adminMode">
              <div class="custom-checkbox" v-show="shouldShowCheckbox(rowIndex)" @click="handleRowCheckboxChange(rowIndex)">
                <span v-if="selectedRows[rowIndex]" class="checkbox-icon checked">✓</span>
                <span v-else class="checkbox-icon unchecked">○</span>
              </div>
            </td>
            <td v-for="header in headers" :key="header.key" v-show="header.key !== 'operation' || adminMode">
              <span v-if="header.key === 'operation'">
                <button class="text-button" @click="handleOperation(rowIndex)">操作</button>
              </span>
              <span v-else>{{ row[header.key] }}</span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <div v-if="showOperationDialog" class="operation-dialog">
      <div class="dialog-content">
        <h3>{{ currentOperation }}操作</h3>
        <p>您正在对第{{ currentRowIndex + 1 }}行数据进行操作</p>
        <div class="row-data" v-if="currentRowData">
          <template v-for="(value, key) in currentRowData" :key="key">
            <div class="data-item" v-if="key !== 'operation'">
              <label class="data-label">{{ getHeaderLabel(key) }}:</label>
              <input 
                v-model="editableRowData[key]" 
                class="data-input" 
                :placeholder="value"
              />
            </div>
          </template>
        </div>
        <div class="dialog-buttons">
          <button @click="confirmOperation" class="confirm-btn">确认</button>
          <button @click="closeOperationDialog" class="cancel-btn">取消</button>
        </div>
      </div>
    </div>
    
  </div>
</template>

<script>
export default {
  name: 'operationTable',
  data() {
    return {
      currentMode: 'visitor',
      selectAll: false,
      showHeaderCheckboxes: false,
      indeterminate: false,
      selectedRows: {},
      showCheckboxes: {},
      currentRowData: null,
      editableRowData: {},
      headers: [
        { 
          label: '姓名', 
          key: 'name', 
          width: 120
        },
        { 
          label: '年龄', 
          key: 'age', 
          width: 80
        },
        { 
          label: '城市', 
          key: 'city',
          width: 100
        },
        { 
          label: '职业', 
          key: 'job',
          width: 120
        },
        { 
          label: '学历', 
          key: 'education',
          width: 100
        },
        { 
          label: '工作经验', 
          key: 'experience',
          width: 120
        },
        { 
          label: '薪资', 
          key: 'salary',
          width: 100
        },
        { 
          label: '部门', 
          key: 'department',
          width: 120
        },
        { 
          label: '入职日期', 
          key: 'hireDate',
          width: 120
        },
        { 
          label: '状态', 
          key: 'status',
          width: 100
        },
        { 
          label: '操作', 
          key: 'operation',
          width: 80
        }
      ],
      rows: [
        { 
          name: '张三', 
          age: '25', 
          city: '北京', 
          job: '工程师', 
          education: '本科', 
          experience: '3年', 
          salary: '15K', 
          department: '技术部', 
          hireDate: '2020-01-15', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '李四', 
          age: '30', 
          city: '上海', 
          job: '设计师', 
          education: '硕士', 
          experience: '5年', 
          salary: '20K', 
          department: '设计部', 
          hireDate: '2019-03-22', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '王五', 
          age: '28', 
          city: '广州', 
          job: '产品经理', 
          education: '本科', 
          experience: '4年', 
          salary: '18K', 
          department: '产品部', 
          hireDate: '2020-07-10', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '赵六', 
          age: '32', 
          city: '深圳', 
          job: '运营', 
          education: '本科', 
          experience: '6年', 
          salary: '16K', 
          department: '运营部', 
          hireDate: '2018-11-05', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '钱七', 
          age: '27', 
          city: '杭州', 
          job: '数据分析师', 
          education: '硕士', 
          experience: '2年', 
          salary: '14K', 
          department: '数据部', 
          hireDate: '2021-02-18', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '孙八', 
          age: '29', 
          city: '成都', 
          job: '测试工程师', 
          education: '本科', 
          experience: '4年', 
          salary: '15K', 
          department: '技术部', 
          hireDate: '2020-05-30', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '周九', 
          age: '31', 
          city: '武汉', 
          job: 'UI设计师', 
          education: '硕士', 
          experience: '6年', 
          salary: '19K', 
          department: '设计部', 
          hireDate: '2018-09-12', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '吴十', 
          age: '26', 
          city: '西安', 
          job: '前端工程师', 
          education: '本科', 
          experience: '2年', 
          salary: '13K', 
          department: '技术部', 
          hireDate: '2021-08-25', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '张三', 
          age: '25', 
          city: '北京', 
          job: '工程师', 
          education: '本科', 
          experience: '3年', 
          salary: '15K', 
          department: '技术部', 
          hireDate: '2020-01-15', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '李四', 
          age: '30', 
          city: '上海', 
          job: '设计师', 
          education: '硕士', 
          experience: '5年', 
          salary: '20K', 
          department: '设计部', 
          hireDate: '2019-03-22', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '王五', 
          age: '28', 
          city: '广州', 
          job: '产品经理', 
          education: '本科', 
          experience: '4年', 
          salary: '18K', 
          department: '产品部', 
          hireDate: '2020-07-10', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '赵六', 
          age: '32', 
          city: '深圳', 
          job: '运营', 
          education: '本科', 
          experience: '6年', 
          salary: '16K', 
          department: '运营部', 
          hireDate: '2018-11-05', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '钱七', 
          age: '27', 
          city: '杭州', 
          job: '数据分析师', 
          education: '硕士', 
          experience: '2年', 
          salary: '14K', 
          department: '数据部', 
          hireDate: '2021-02-18', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '孙八', 
          age: '29', 
          city: '成都', 
          job: '测试工程师', 
          education: '本科', 
          experience: '4年', 
          salary: '15K', 
          department: '技术部', 
          hireDate: '2020-05-30', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '周九', 
          age: '31', 
          city: '武汉', 
          job: 'UI设计师', 
          education: '硕士', 
          experience: '6年', 
          salary: '19K', 
          department: '设计部', 
          hireDate: '2018-09-12', 
          status: '在职',
          operation: '操作'
        },
        { 
          name: '吴十', 
          age: '26', 
          city: '西安', 
          job: '前端工程师', 
          education: '本科', 
          experience: '2年', 
          salary: '13K', 
          department: '技术部', 
          hireDate: '2021-08-25', 
          status: '在职',
          operation: '操作'
        }
      ],
      headerRefs: [],
      resizing: false,
      startX: 0,
      startWidthLeft: 0,
      startWidthRight: 0,
      leftIndex: -1, 
      rightIndex: -1,
      showOperationDialog: false,
      currentOperation: '',
      currentRowIndex: -1
    };
  },
  computed: {
    visitorMode() {
      return this.currentMode === 'visitor';
    },
    adminMode() {
      return this.currentMode === 'admin';
    },
    showOperationColumn() {
      return this.adminMode;
    }
  },
  mounted() {
    document.addEventListener('mousemove', this.onMouseMove);
    document.addEventListener('mouseup', this.onMouseUp);
  },
  beforeUnmount() {
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('mouseup', this.onMouseUp);
  },
  methods: {
    showHeaderCheckbox() {
      this.showHeaderCheckboxes = true;
    },
    hideHeaderCheckbox() {
      if (!this.hasSelectedRows() && !this.selectAll) {
        this.showHeaderCheckboxes = false;
      }
    },
    handleSelectAllChange() { 
      if (this.indeterminate) {
        this.selectAll = true;
        this.indeterminate = false;
        this.showHeaderCheckboxes = true;
        for (let i = 0; i < this.rows.length; i++) {
          this.selectedRows[i] = true;
          this.showCheckboxes[i] = true;
        }
      } else if (this.selectAll) {
        this.selectAll = false;
        this.indeterminate = false;
        this.showHeaderCheckboxes = false;
        for (let i = 0; i < this.rows.length; i++) {
          this.selectedRows[i] = false;
          this.showCheckboxes[i] = false;
        }
      } else {
        this.selectAll = true;
        this.indeterminate = false;
        this.showHeaderCheckboxes = true;
        for (let i = 0; i < this.rows.length; i++) {
          this.selectedRows[i] = true;
          this.showCheckboxes[i] = true;
        }
      }
      this.selectedRows = { ...this.selectedRows };
      this.showCheckboxes = { ...this.showCheckboxes };
    },
    showCheckbox(rowIndex) {
      this.showCheckboxes = { ...this.showCheckboxes, [rowIndex]: true };
    },
    hideCheckbox(rowIndex) {
      if (!this.selectedRows[rowIndex] && !this.selectAll) {
        this.showCheckboxes = { ...this.showCheckboxes, [rowIndex]: false };
      }
    },
    shouldShowCheckbox(rowIndex) {
      const hasSelected = this.hasSelectedRows();
      if (hasSelected) {
        this.showHeaderCheckboxes = true;
      }
      return this.selectAll || hasSelected || this.showCheckboxes[rowIndex];
    },
    hasSelectedRows() {
      return Object.values(this.selectedRows).some(selected => selected);
    },
    
    handleRowCheckboxChange(rowIndex) {
      for (let i = 0; i < this.rows.length; i++) {
        if (this.selectedRows[i] === undefined) {
          this.selectedRows[i] = false;
        }
      }
      
      this.selectedRows[rowIndex] = !this.selectedRows[rowIndex];
      
      const selectedValues = Object.values(this.selectedRows);
      const allSelected = selectedValues.every(selected => selected);
      const hasSelected = selectedValues.some(selected => selected);
      
      this.selectAll = allSelected;
      this.indeterminate = !allSelected && hasSelected;
      
      if (!hasSelected && !this.selectAll) {
        this.showHeaderCheckboxes = false;
      } else if (hasSelected) {
        this.showHeaderCheckboxes = true;
      }
      
      this.selectedRows = { ...this.selectedRows };
      this.showCheckboxes = { ...this.showCheckboxes };
    },
    handleOperation(rowIndex) {
      this.currentOperation = '操作';
      this.currentRowIndex = rowIndex;
      this.currentRowData = this.rows[rowIndex];
      
      this.editableRowData = { ...this.rows[rowIndex] };
      
      this.showOperationDialog = true;
    },
    getHeaderLabel(key) {
      const header = this.headers.find(h => h.key === key);
      return header ? header.label : key;
    },
    closeOperationDialog() {
      this.showOperationDialog = false;
      this.currentOperation = '';
      this.currentRowIndex = -1;
      this.currentRowData = null;
      this.editableRowData = {};
    },
    confirmOperation() {
      if (this.currentRowIndex >= 0 && this.currentRowIndex < this.rows.length) {
        const operationValue = this.rows[this.currentRowIndex].operation;
        this.rows.splice(this.currentRowIndex, 1, { 
          ...this.editableRowData, 
          operation: operationValue 
        });
      }
      this.closeOperationDialog();
      
      this.selectedRows = { ...this.selectedRows };
    },
    startResize(event, index) {
      this.resizing = true;
      this.leftIndex = index;
      this.rightIndex = index + 1;
      this.startX = event.clientX;
      this.startWidthLeft = this.headers[this.leftIndex].width;
      this.startWidthRight = this.headers[this.rightIndex].width;
      
      event.preventDefault();
    },
    
    onMouseMove(event) {
      if (!this.resizing) return;
      
      document.body.classList.add('resizing');
      
      const diff = event.clientX - this.startX;
      
      const totalWidth = this.startWidthLeft + this.startWidthRight;
      let newWidthLeft = this.startWidthLeft + diff;
      let newWidthRight = this.startWidthRight - diff;
      
      if (newWidthLeft < 50) {
        newWidthLeft = 50;
        newWidthRight = totalWidth - 50;
      } else if (newWidthRight < 50) {
        newWidthRight = 50;
        newWidthLeft = totalWidth - 50;
      }
      
      this.headers[this.leftIndex].width = newWidthLeft;
      this.headers[this.rightIndex].width = newWidthRight;
    },
    
    onMouseUp() {
      if (this.resizing) {
        this.resizing = false;
        this.leftIndex = -1;
        this.rightIndex = -1;
        
        document.body.classList.remove('resizing');
      }
    },
    
    onTableScroll(event) {
      if (this.$refs.headerTable) {
        this.$refs.headerTable.style.transform = \`translateX(\${-event.target.scrollLeft}px)\`;
      }
    }
  }
};
</script>

<style scoped>
.test-table {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.table-header-wrapper {
  overflow: hidden;
  flex-shrink: 0;
  background: #2d2d2d;
}

.header-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

.table-body-wrapper {
  overflow-x: auto;
  overflow-y: scroll; 
  flex-grow: 1;
  margin-top: -1px; 
  max-height: 400px; 
  
  &::-webkit-scrollbar {
    width: 0px; 
    height: 12px;
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #2d2d2d;
    
    &:hover {
      background-color: #777;
    }
  }

  &::-webkit-scrollbar-corner {
    background-color: #2d2d2d;
  }
}

.body-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

th,
td {
  padding: 12px 10px;
  text-align: left;
  border-bottom: 1px solid #444;
  border-right: 1px solid #444;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  box-sizing: border-box;
}

th:last-child,
td:last-child {
  border-right: none;
}

th {
  color: white;
  font-weight: bold;
  position: relative;
  user-select: none;
  border-right: 1px solid #444;
  background: #2d2d2d;
}

tr:hover {
  background-color: #3a3a3a;
}

.resizable-header {
  padding: 0;
}

.header-content {
  padding: 12px 15px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resize-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}

body.resizing {
  cursor: col-resize;
}

.checkbox-header,
.checkbox-cell {
  width: 40px;
  text-align: center;
  padding: 12px 0;
}

.checkbox-cell input[type="checkbox"] {
  cursor: pointer;
}

.custom-checkbox {
  display: inline-block;
  width: 18px;
  height: 18px;
  cursor: pointer;
  position: relative;
  background: #333;
  border: 1px solid #555;
  border-radius: 3px;
}

.checkbox-icon {
  display: inline-block;
  width: 100%;
  height: 100%;
  line-height: 18px;
  font-size: 14px;
  font-weight: bold;
  border-radius: 3px;
}

.checkbox-icon.checked {
  background-color: #409eff;
  color: white;
}

.checkbox-icon.indeterminate {
  background-color: #409eff;
  color: white;
}

.checkbox-icon.unchecked {
  border: 1px solid #dcdfe6;
  color: transparent;
}

.mode-checkboxes {
  display: flex;
  gap: 20px;
  margin-bottom: 15px;
  padding: 10px 0;
}

.mode-checkbox {
  display: flex;
  align-items: center;
  cursor: pointer;
  color: #e0e0e0;
  font-size: 14px;
}

.mode-checkbox input[type="checkbox"] {
  margin-right: 8px;
  transform: scale(1.2);
  cursor: pointer;
}

.text-button {
  background: none;
  border: none;
  color: #409eff;
  cursor: pointer;
  font-size: 14px;
  padding: 0;
  text-decoration: underline;
}

.text-button:hover {
  color: #66b1ff;
}

.operation-dialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.dialog-content {
  background-color: #2d2d2d;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
  min-width: 400px;
  max-width: 600px;
  max-height: 80vh;
  overflow-y: auto;
  border: 1px solid #444;
}

.dialog-content::-webkit-scrollbar {
  width: 8px;
}

.dialog-content::-webkit-scrollbar-track {
  background: #2a2a2a;
  border-radius: 4px;
}

.dialog-content::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 4px;
}

.dialog-content::-webkit-scrollbar-thumb:hover {
  background: #666;
}

.dialog-content h3 {
  margin-top: 0;
  color: #e0e0e0;
}

.dialog-content p {
  color: #aaa;
  margin-bottom: 20px;
}

.row-data {
  margin: 20px 0;
  padding: 15px;
  background-color: #3a3a3a;
  border-radius: 4px;
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #444;
}

.row-data::-webkit-scrollbar {
  width: 6px;
}

.row-data::-webkit-scrollbar-track {
  background: #333;
  border-radius: 3px;
}

.row-data::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 3px;
}

.row-data::-webkit-scrollbar-thumb:hover {
  background: #666;
}

.data-item {
  display: flex;
  margin-bottom: 15px;
  padding: 8px 0;
  border-bottom: 1px solid #444;
  align-items: center;
}

.data-item:last-child {
  border-bottom: none;
}

.data-label {
  font-weight: bold;
  min-width: 80px;
  color: #e0e0e0;
  margin-right: 10px;
}

.data-input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #555;
  border-radius: 4px;
  background-color: #333;
  color: #e0e0e0;
  font-size: 14px;
}

.data-input:focus {
  outline: none;
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}

.dialog-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
}

.confirm-btn,
.cancel-btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.confirm-btn {
  background-color: #409eff;
  color: white;
}

.cancel-btn {
  background-color: #f5f5f5;
  color: #333;
}
</style>`,
    '含过滤器的table表格':`<template>
  <div class="test-table" ref="tableContainer">
    
    <div class="table-header-wrapper">
      <table ref="headerTable" class="header-table">
        <colgroup>
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" />
        </colgroup>
        <thead>
          <tr>
            <th v-for="(header, index) in headers" :key="header.key" class="resizable-header" :ref="el => { if (el) headerRefs[index] = el; }">
              <div class="header-content">{{ header.label }}</div>
              <div 
                v-if="index < headers.length - 1"
                class="resize-handle" 
                @mousedown="startResize($event, index)"
              ></div>
            </th>
          </tr>
        </thead>
      </table>
    </div>

    <div class="filter-row-wrapper">
      <table class="filter-table">
        <colgroup>
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" />
        </colgroup>
        <tbody>
          <tr>
            <td v-for="(header, index) in headers" :key="header.key">
              <div class="filter-container" v-if="header.type === 'select' && header.filter && header.filter.options">
                <select 
                  :value="filterValues[header.key]" 
                  @change="handleFilterChange(header.key, $event.target.value)"
                  class="filter-select"
                >
                  <option value="">全部</option>
                  <option v-for="option in header.filter.options" :key="option.value" :value="option.value">
                    {{ option.label }}
                  </option>
                </select>
              </div>
              
              <div class="filter-container filter-with-operators" v-else-if="header.type === 'operators'">
                <select 
                  :value="operatorValues[header.key]" 
                  @change="handleOperatorChange(header.key, $event.target.value)"
                  class="operator-select"
                >
                  <option v-for="operator in operatorOptions" :key="operator.value" :value="operator.value">
                    {{ operator.label }}
                  </option>
                </select>
                <input 
                  :value="filterValues[header.key]" 
                  @input="handleFilterChange(header.key, $event.target.value)"
                  class="operators-filter-input"
                  placeholder="请输入筛选条件"
                />
              </div>
              
              <div class="filter-container" v-else-if="header.type === 'input'">
                <input 
                  :value="filterValues[header.key]" 
                  @input="handleFilterChange(header.key, $event.target.value)"
                  class="input-filter-input"
                  :placeholder="header.label"
                />
              </div>
            
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <div class="table-body-wrapper" ref="tableBodyWrapper" @scroll="onTableScroll">
      <table ref="bodyTable" class="body-table">
        <colgroup>
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" />
        </colgroup>
        <tbody>
          <tr v-for="(row, rowIndex) in filteredRows" :key="rowIndex">
            <td v-for="(header, cellIndex) in headers" :key="header.key">{{ row[header.key] }}</td>
          </tr>
          <tr v-if="filteredRows.length === 0">
            <td :colspan="headers.length" class="no-data-cell">
              <div class="no-data">暂无数据</div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>

  </div>
</template>

<script>
export default {
  name: 'filterTable',
  data() {
    return {
      headers: [
        { 
          label: '姓名', 
          key: 'name', 
          type: 'input',
          width: 120,
          filter: {
            options: [
              { value: '张三', label: '张三' },
              { value: '李四', label: '李四' },
              { value: '王五', label: '王五' },
              { value: '赵六', label: '赵六' },
              { value: '钱七', label: '钱七' },
              { value: '孙八', label: '孙八' },
              { value: '周九', label: '周九' },
              { value: '吴十', label: '吴十' }
            ]
          }
        },
        { 
          label: '年龄', 
          key: 'age', 
          type: 'operators',
          width: 120
        },
        { 
          label: '城市', 
          key: 'city',
          width: 120,
          type: 'select',
          filter: {
            options: [
              { value: '北京', label: '北京' },
              { value: '上海', label: '上海' },
              { value: '广州', label: '广州' },
              { value: '深圳', label: '深圳' },
              { value: '杭州', label: '杭州' },
              { value: '成都', label: '成都' },
              { value: '武汉', label: '武汉' },
              { value: '西安', label: '西安' }
            ]
          }
        },
        { 
          label: '职业', 
          key: 'job',
          width: 120,
          type: 'select',
          filter: {
            options: [
              { value: '工程师', label: '工程师' },
              { value: '设计师', label: '设计师' },
              { value: '产品经理', label: '产品经理' },
              { value: '运营', label: '运营' },
              { value: '数据分析师', label: '数据分析师' },
              { value: '测试工程师', label: '测试工程师' },
              { value: 'UI设计师', label: 'UI设计师' },
              { value: '前端工程师', label: '前端工程师' }
            ]
          }
        },
        { 
          label: '学历', 
          key: 'education',
          width: 120,
          type: 'select',
          filter: {
            options: [
              { value: '本科', label: '本科' },
              { value: '硕士', label: '硕士' }
            ]
          }
        },
        { 
          label: '工作经验', 
          key: 'experience',
          width: 120,        
          type: 'operators',
        },
        { 
          label: '薪资', 
          key: 'salary',
          width: 120,
          type: 'operators',
        },
        { 
          label: '部门', 
          key: 'department',
          width: 120,
          type: 'select',
          filter: {
            options: [
              { value: '技术部', label: '技术部' },
              { value: '设计部', label: '设计部' },
              { value: '产品部', label: '产品部' },
              { value: '运营部', label: '运营部' },
              { value: '数据部', label: '数据部' }
            ]
          }
        },
        { 
          label: '入职日期', 
          key: 'hireDate',
          width: 120
        },
        { 
          label: '状态', 
          key: 'status',
          width: 120,
          type: 'select',
          filter: {
            options: [
              { value: '在职', label: '在职' },
              { value: '离职', label: '离职' }
            ]
          }
        }
      ],
      filterValues: {},
      operatorValues: {},
      operatorOptions: [
        { value: 'equal', label: '=' },
        { value: 'greater', label: '>' },
        { value: 'less', label: '<' },
      ],
      rows: [
        { name: '张三', age: '25', city: '北京', job: '工程师', education: '本科', experience: '3年', salary: '15K', department: '技术部', hireDate: '2020-01-15', status: '在职' },
        { name: '李四', age: '30', city: '上海', job: '设计师', education: '硕士', experience: '5年', salary: '20K', department: '设计部', hireDate: '2019-03-22', status: '在职' },
        { name: '王五', age: '28', city: '广州', job: '产品经理', education: '本科', experience: '4年', salary: '18K', department: '产品部', hireDate: '2020-07-10', status: '在职' },
        { name: '赵六', age: '32', city: '深圳', job: '运营', education: '本科', experience: '6年', salary: '16K', department: '运营部', hireDate: '2018-11-05', status: '在职' },
        { name: '钱七', age: '27', city: '杭州', job: '数据分析师', education: '硕士', experience: '2年', salary: '14K', department: '数据部', hireDate: '2021-02-18', status: '在职' },
        { name: '孙八', age: '29', city: '成都', job: '测试工程师', education: '本科', experience: '4年', salary: '15K', department: '技术部', hireDate: '2020-05-30', status: '在职' },
        { name: '周九', age: '31', city: '武汉', job: 'UI设计师', education: '硕士', experience: '6年', salary: '19K', department: '设计部', hireDate: '2018-09-12', status: '在职' },
        { name: '吴十', age: '26', city: '西安', job: '前端工程师', education: '本科', experience: '2年', salary: '13K', department: '技术部', hireDate: '2021-08-25', status: '在职' },
        { name: '张三', age: '25', city: '北京', job: '工程师', education: '本科', experience: '3年', salary: '15K', department: '技术部', hireDate: '2020-01-15', status: '在职' },
        { name: '李四', age: '30', city: '上海', job: '设计师', education: '硕士', experience: '5年', salary: '20K', department: '设计部', hireDate: '2019-03-22', status: '在职' },
        { name: '王五', age: '28', city: '广州', job: '产品经理', education: '本科', experience: '4年', salary: '18K', department: '产品部', hireDate: '2020-07-10', status: '在职' },
        { name: '赵六', age: '32', city: '深圳', job: '运营', education: '本科', experience: '6年', salary: '16K', department: '运营部', hireDate: '2018-11-05', status: '在职' },
        { name: '钱七', age: '27', city: '杭州', job: '数据分析师', education: '硕士', experience: '2年', salary: '14K', department: '数据部', hireDate: '2021-02-18', status: '在职' },
        { name: '孙八', age: '29', city: '成都', job: '测试工程师', education: '本科', experience: '4年', salary: '15K', department: '技术部', hireDate: '2020-05-30', status: '在职' },
        { name: '周九', age: '31', city: '武汉', job: 'UI设计师', education: '硕士', experience: '6年', salary: '19K', department: '设计部', hireDate: '2018-09-12', status: '在职' },
        { name: '吴十', age: '26', city: '西安', job: '前端工程师', education: '本科', experience: '2年', salary: '13K', department: '技术部', hireDate: '2021-08-25', status: '在职' }
      ],
      headerRefs: [],
      resizing: false,
      startX: 0,
      startWidthLeft: 0,
      startWidthRight: 0,
      leftIndex: -1, 
      rightIndex: -1
    };
  },
  mounted() {
    document.addEventListener('mousemove', this.onMouseMove);
    document.addEventListener('mouseup', this.onMouseUp);
  },
  beforeUnmount() {
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('mouseup', this.onMouseUp);
  },
  computed: {
    filteredRows() {
      return this.rows.filter(row => {
        return this.headers.every(header => {
          const filterValue = this.filterValues[header.key];
          if (!filterValue) return true;
          
          if (header.type === 'select' && header.filter && header.filter.options) {
            return row[header.key] === filterValue;
          } else if (header.type === 'operators') {
            const operator = this.operatorValues[header.key] || 'equal';
            const cellValue = row[header.key];
            
            const cellNum = parseFloat(cellValue);
            const filterNum = parseFloat(filterValue);
            
            if (isNaN(cellNum) || isNaN(filterNum)) return false;
            
            switch (operator) {
              case 'equal': return cellNum === filterNum;
              case 'notEqual': return cellNum !== filterNum;
              case 'greater': return cellNum > filterNum;
              case 'less': return cellNum < filterNum;
              default: return cellValue.includes(filterValue);
            }
          } else if (header.type === 'input' || header.filter ) {
            return !filterValue || row[header.key].includes(filterValue);
          } else {
            return !filterValue || row[header.key].includes(filterValue);
          }
        });
      });
    }
  },
  methods: {
      
      handleFilterChange(key, value) {
        if (!this.filterValues) {
          this.filterValues = {};
        }
        this.filterValues[key] = value;
      },
      
      handleOperatorChange(key, value) {
        if (!this.operatorValues) {
          this.operatorValues = {};
        }
        this.operatorValues[key] = value;
      },
      
      startResize(event, index) {
      this.resizing = true;
      this.leftIndex = index;
      this.rightIndex = index + 1;
      this.startX = event.clientX;
      this.startWidthLeft = this.headers[this.leftIndex].width;
      this.startWidthRight = this.headers[this.rightIndex].width;
      
      event.preventDefault();
    },
    
    onMouseMove(event) {
      if (!this.resizing) return;
      
      document.body.classList.add('resizing');
      
      const diff = event.clientX - this.startX;
      
      const totalWidth = this.startWidthLeft + this.startWidthRight;
      let newWidthLeft = this.startWidthLeft + diff;
      let newWidthRight = this.startWidthRight - diff;
      
      if (newWidthLeft < 50) {
        newWidthLeft = 50;
        newWidthRight = totalWidth - 50;
      } else if (newWidthRight < 50) {
        newWidthRight = 50;
        newWidthLeft = totalWidth - 50;
      }
      
      this.headers[this.leftIndex].width = newWidthLeft;
      this.headers[this.rightIndex].width = newWidthRight;
    },
    
    onMouseUp() {
      if (this.resizing) {
        this.resizing = false;
        this.leftIndex = -1;
        this.rightIndex = -1;
        
        document.body.classList.remove('resizing');
      }
    },
    
    onTableScroll(event) {
      if (this.$refs.headerTable) {
        this.$refs.headerTable.style.transform = \`translateX(\${-event.target.scrollLeft}px)\`;
      }
      
      const filterRow = this.$el.querySelector('.filter-table');
      if (filterRow) {
        filterRow.style.transform = \`translateX(\${-event.target.scrollLeft}px)\`;
      }
    }
  }
};
</script>

<style scoped>
.test-table {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.table-header-wrapper {
  overflow: hidden;
  flex-shrink: 0;
}

.header-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

.filter-row-wrapper {
  overflow: hidden;
  flex-shrink: 0;
  background-color: #3a3a3a;
}

.filter-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

.table-body-wrapper {
  overflow-x: auto;
  overflow-y: scroll; 
  flex-grow: 1;
  margin-top: -1px; 
  max-height: 400px; 
  
  &::-webkit-scrollbar {
    width: 0px; 
    height: 12px;
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #2d2d2d;
    
    &:hover {
      background-color: #777;
    }
  }

  &::-webkit-scrollbar-corner {
    background-color: #2d2d2d;
  }
}

.no-data-cell {
  text-align: center;
  padding: 0;
  background-color: #333;
}

.no-data {
  text-align: center;
  padding: 40px 0;
  color: #888;
  font-size: 16px;
  background-color: #333;
}

.body-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

th,
td {
  padding: 12px 10px;
  text-align: left;
  border-bottom: 1px solid #444;
  border-right: 1px solid #444;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  box-sizing: border-box;
}

th:last-child,
td:last-child {
  border-right: none;
}

th {
  color: white;
  font-weight: bold;
  position: relative;
  user-select: none;
  border-right: 1px solid #444;
}

tr:hover {
  background-color: #3a3a3a;
}

.resizable-header {
  padding: 0;
}

.header-content {
  padding: 12px 15px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resize-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}

body.resizing {
  cursor: col-resize;
  user-select: none;
}

.filter-container {
  display: flex;
  gap: 2px;
  background-color: #555;
  border: 1px solid #666;
  border-radius: 4px;
  padding: 2px;
}

.filter-container:hover {
  background-color: #666;
  border-color: #777;
}

.filter-container:focus-within {
  border-color: #888;
}

.filter-select,
.operator-select,
.operators-filter-input,
.input-filter-input {
  background-color: #555;
  color: white;
  border: none;
  outline: none;
  padding: 4px;
  border-radius: 2px;
}

.filter-select:focus,
.operator-select:focus,
.operators-filter-input:focus,
.input-filter-input:focus {
  background-color: #666;
  box-shadow: 0 0 3px rgba(255, 255, 255, 0.5);
}

.filter-select option,
.operator-select option {
  background-color: #555;
  color: white;
  border-radius: 4px;
}

.filter-select {
  width: 100%;
}

.operator-select {
  width: 40px;
}

.operators-filter-input {
  width: 100%;
}

.input-filter-input {
  width: 100%;
}
</style>`,
    '含复杂模态框的table表格':`<template>
  <div class="test-table" ref="tableContainer">
    <div class="table-header-wrapper">
      <table ref="headerTable" class="header-table">
        <colgroup>
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" />
        </colgroup>
        <thead>
          <tr>
            <th v-for="(header, index) in headers" :key="header.key" class="resizable-header" :ref="el => { if (el) headerRefs[index] = el; }">
              <div class="header-content">{{ header.label }}</div>
              <div 
                v-if="index < headers.length - 1"
                class="resize-handle" 
                @mousedown="startResize($event, index)"
              ></div>
            </th>
          </tr>
        </thead>
      </table>
    </div>

    <div class="table-body-wrapper" ref="tableBodyWrapper" @scroll="onTableScroll">
      <table ref="bodyTable" class="body-table">
        <colgroup>
          <col v-for="header in headers" :key="header.key" :style="{ width: header.width + 'px' }" />
        </colgroup>
        <tbody>
          <tr v-for="(row, rowIndex) in rows" :key="rowIndex">
            <td v-for="header in headers" :key="header.key">
              <span v-if="header.key === 'operation'">
                <button class="text-button" @click="handleOperation(rowIndex)">操作</button>
              </span>
              <span v-else>{{ row[header.key] }}</span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <div v-if="showOperationDialog" class="operation-dialog">
      <div class="dialog-content">
        <h3>{{ currentOperation }}操作</h3>
        <p>您正在对第{{ currentRowIndex + 1 }}行数据进行操作</p>
        <div class="row-data" v-if="currentRowData">
          <div class="data-item">
            <label class="data-label">姓名:</label>
            <input 
              v-model="editableRowData.name" 
              class="data-input" 
              :placeholder="currentRowData.name"
            />
          </div>
          <div class="data-item">
            <label class="data-label">城市:</label>
            <input 
              v-model="editableRowData.city" 
              class="data-input" 
              :placeholder="currentRowData.city"
            />
          </div>
          <div class="data-item">
            <label class="data-label">工作过的城市:</label>
            <input
              class="data-input" 
              placeholder="请勾选之前工作过的城市"
              readonly
              @click="openWorkCitiesModal('workCities')"
            />
          </div>
          <div class="selected-cities-tags" v-if="getSelectedCities('workCities').length > 0">
            <template v-for="(city, index) in getSelectedCities('workCities')" :key="index">
              <span class="city-tag">
                {{ city }}
                <button 
                  @click="removeCity('workCities', city)" 
                  class="remove-city-btn"
                >
                  ×
                </button>
              </span>
            </template>
          </div>
          <div class="data-item">
            <label class="data-label">职位:</label>
            <input 
              v-model="editableRowData.position" 
              class="data-input" 
              :placeholder="currentRowData.position"
            />
          </div>
        </div>
        <div class="dialog-buttons">
          <button @click="confirmOperation" class="confirm-btn">确认</button>
          <button @click="closeOperationDialog" class="cancel-btn">取消</button>
        </div>
      </div>
    </div>
    
    <!-- 工作城市选择模态框 -->
    <div v-if="showWorkCitiesModal" class="work-cities-dialog">
      <div class="work-cities-content">
        <h3>选择工作过的城市</h3>
        <div class="work-cities-table-container">
          <table class="work-cities-table">
            <thead>
              <tr>
                <th>选择</th>
                <th>城市名称</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(city, index) in allCities" :key="index">
                <td>
                  <input 
                    type="checkbox" 
                    :value="city" 
                    v-model="selectedWorkCities"
                  />
                </td>
                <td>{{ city }}</td>
              </tr>
            </tbody>
          </table>
        </div>
        <div class="work-cities-buttons">
          <button @click="confirmWorkCities" class="confirm-btn">确认</button>
          <button @click="closeWorkCitiesModal" class="cancel-btn">取消</button>
        </div>
      </div>
    </div>
    
  </div>
</template>

<script>
export default {
  name: 'modalTable',
  data() {
    return {
      currentRowData: null,
      editableRowData: {},
      headers: [
        { 
          label: '姓名', 
          key: 'name', 
          width: 120
        },
        { 
          label: '城市', 
          key: 'city', 
          width: 120
        },
        { 
          label: '工作过的城市', 
          key: 'workCities',
          width: 120
        },
        { 
          label: '职位', 
          key: 'position',
          width: 120
        },
        { 
          label: '操作', 
          key: 'operation',
          width: 80
        },
      ],
      rows: [
        { 
          name: '张三', 
          city: '北京', 
          workCities: '',
          position: '前端开发',
          operation: '操作'
        },
        { 
          name: '李四', 
          city: '上海', 
          workCities: '',
          position: '后端开发', 
          operation: '操作'
        },
        { 
          name: '王五', 
          city: '广州', 
          workCities: '',
          position: '前端开发', 
          operation: '操作'
        },
        { 
          name: '赵六', 
          city: '深圳', 
          workCities: '',
          position: '前端开发', 
          operation: '操作'
        },
        { 
          name: '钱七', 
          city: '杭州', 
          workCities: '', 
          position: '前端开发', 
          operation: '操作'
        }
      ],
      headerRefs: [],
      resizing: false,
      startX: 0,
      startWidthLeft: 0,
      startWidthRight: 0,
      leftIndex: -1, 
      rightIndex: -1,
      showOperationDialog: false,
      currentOperation: '',
      currentRowIndex: -1,
      // 工作城市模态框相关数据
      showWorkCitiesModal: false,
      selectedWorkCities: [],
      currentWorkCitiesField: '',
      allCities: [
        '北京', '上海', '广州', '深圳', '杭州', 
        '成都', '南京', '武汉', '西安', '重庆',
        '天津', '苏州', '长沙', '郑州', '青岛'
      ]
    };
  },
  computed: {
  },
  mounted() {
    document.addEventListener('mousemove', this.onMouseMove);
    document.addEventListener('mouseup', this.onMouseUp);
  },
  beforeUnmount() {
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('mouseup', this.onMouseUp);
  },
  methods: {
    handleOperation(rowIndex) {
      this.currentOperation = '操作';
      this.currentRowIndex = rowIndex;
      this.currentRowData = this.rows[rowIndex];
      
      this.editableRowData = { ...this.rows[rowIndex] };
      
      this.showOperationDialog = true;
    },
    
    // 打开工作城市选择模态框
    openWorkCitiesModal(field) {
      this.currentWorkCitiesField = field;
      // 初始化已选择的城市
      const workCitiesStr = this.editableRowData[field] || '';
      this.selectedWorkCities = workCitiesStr ? workCitiesStr.split(',') : [];
      this.showWorkCitiesModal = true;
    },
    
    // 关闭工作城市选择模态框
    closeWorkCitiesModal() {
      this.showWorkCitiesModal = false;
      this.selectedWorkCities = [];
      this.currentWorkCitiesField = '';
    },
    
    // 确认工作城市选择
    confirmWorkCities() {
      // 将选中的城市更新到editableRowData中
      this.editableRowData[this.currentWorkCitiesField] = this.selectedWorkCities.join(',');
      this.closeWorkCitiesModal();
    },
    
    // 获取选中的城市列表
    getSelectedCities(field) {
      const citiesStr = this.editableRowData[field] || '';
      return citiesStr ? citiesStr.split(',') : [];
    },
    
    // 删除选中的城市
    removeCity(field, cityToRemove) {
      const cities = this.getSelectedCities(field);
      const updatedCities = cities.filter(city => city !== cityToRemove);
      this.editableRowData[field] = updatedCities.join(',');
    },
    getHeaderLabel(key) {
      const header = this.headers.find(h => h.key === key);
      return header ? header.label : key;
    },
    closeOperationDialog() {
      this.showOperationDialog = false;
      this.currentOperation = '';
      this.currentRowIndex = -1;
      this.currentRowData = null;
      this.editableRowData = {};
    },
    confirmOperation() {
      if (this.currentRowIndex >= 0 && this.currentRowIndex < this.rows.length) {
        const operationValue = this.rows[this.currentRowIndex].operation;
        this.rows.splice(this.currentRowIndex, 1, { 
          ...this.editableRowData, 
          operation: operationValue 
        });
      }
      this.closeOperationDialog();
    },
    startResize(event, index) {
      this.resizing = true;
      this.leftIndex = index;
      this.rightIndex = index + 1;
      this.startX = event.clientX;
      this.startWidthLeft = this.headers[this.leftIndex].width;
      this.startWidthRight = this.headers[this.rightIndex].width;
      
      event.preventDefault();
    },
    
    onMouseMove(event) {
      if (!this.resizing) return;
      
      document.body.classList.add('resizing');
      
      const diff = event.clientX - this.startX;
      
      const totalWidth = this.startWidthLeft + this.startWidthRight;
      let newWidthLeft = this.startWidthLeft + diff;
      let newWidthRight = this.startWidthRight - diff;
      
      if (newWidthLeft < 50) {
        newWidthLeft = 50;
        newWidthRight = totalWidth - 50;
      } else if (newWidthRight < 50) {
        newWidthRight = 50;
        newWidthLeft = totalWidth - 50;
      }
      
      this.headers[this.leftIndex].width = newWidthLeft;
      this.headers[this.rightIndex].width = newWidthRight;
    },
    
    onMouseUp() {
      if (this.resizing) {
        this.resizing = false;
        this.leftIndex = -1;
        this.rightIndex = -1;
        
        document.body.classList.remove('resizing');
      }
    },
    
    onTableScroll(event) {
      if (this.$refs.headerTable) {
        this.$refs.headerTable.style.transform = \`translateX(\${-event.target.scrollLeft}px)\`;
      }
    }
  }
};
</script>

<style scoped>
.test-table {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
}

.table-header-wrapper {
  overflow: hidden;
  flex-shrink: 0;
  background: #2d2d2d;
}

.header-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

.table-body-wrapper {
  overflow-x: auto;
  overflow-y: scroll; 
  flex-grow: 1;
  margin-top: -1px; 
  max-height: 400px; 
  
  &::-webkit-scrollbar {
    width: 0px; 
    height: 12px;
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-track {
    background-color: #2d2d2d;
    border-radius: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #555;
    border-radius: 6px;
    border: 2px solid #2d2d2d;
    
    &:hover {
      background-color: #777;
    }
  }

  &::-webkit-scrollbar-corner {
    background-color: #2d2d2d;
  }
}

.body-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
  table-layout: fixed;
}

th,
td {
  padding: 12px 10px;
  text-align: left;
  border-bottom: 1px solid #444;
  border-right: 1px solid #444;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  box-sizing: border-box;
}

th:last-child,
td:last-child {
  border-right: none;
}

th {
  color: white;
  font-weight: bold;
  position: relative;
  user-select: none;
  border-right: 1px solid #444;
  background: #2d2d2d;
}

tr:hover {
  background-color: #3a3a3a;
}

.resizable-header {
  padding: 0;
}

.header-content {
  padding: 12px 15px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.resize-handle {
  position: absolute;
  top: 0;
  right: -5px;
  width: 10px;
  height: 100%;
  cursor: col-resize;
  z-index: 1;
}

body.resizing {
  cursor: col-resize;
}

.text-button {
  background: none;
  border: none;
  color: #409eff;
  cursor: pointer;
  font-size: 14px;
  padding: 0;
  text-decoration: underline;
}

.text-button:hover {
  color: #66b1ff;
}

.operation-dialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.dialog-content {
  background-color: #2d2d2d;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
  width: 600px;
  max-height: 80vh;
  overflow-y: auto;
  border: 1px solid #444;
}

.dialog-content::-webkit-scrollbar {
  width: 8px;
}

.dialog-content::-webkit-scrollbar-track {
  background: #2a2a2a;
  border-radius: 4px;
}

.dialog-content::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 4px;
}

.dialog-content::-webkit-scrollbar-thumb:hover {
  background: #666;
}

.dialog-content h3 {
  margin-top: 0;
  color: #e0e0e0;
}

.dialog-content p {
  color: #aaa;
  margin-bottom: 20px;
}

.row-data {
  margin: 20px 0;
  padding: 15px;
  background-color: #3a3a3a;
  border-radius: 4px;
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #444;
}

.row-data::-webkit-scrollbar {
  width: 6px;
}

.row-data::-webkit-scrollbar-track {
  background: #333;
  border-radius: 3px;
}

.row-data::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 3px;
}

.row-data::-webkit-scrollbar-thumb:hover {
  background: #666;
}

.data-item {
  display: flex;
  margin-bottom: 0;
  padding: 8px 0;
  border-bottom: none;
  align-items: center;
  position: relative;
}

.data-item:last-child {
  border-bottom: none;
}

.data-label {
  font-weight: bold;
  min-width: 90px;
  color: #e0e0e0;
  margin-right: 10px;
}

.data-input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #555;
  border-radius: 4px;
  background-color: #333;
  color: #e0e0e0;
  font-size: 14px;
}

.data-input:focus {
  outline: none;
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}

.dialog-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
}

.confirm-btn,
.cancel-btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.confirm-btn {
  background-color: #409eff;
  color: white;
}

.work-cities-dialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1001;
}

.work-cities-content {
  background-color: #2d2d2d;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
  min-width: 500px;
  max-width: 600px;
  max-height: 80vh;
  overflow-y: auto;
  border: 1px solid #444;
}

.work-cities-content h3 {
  margin-top: 0;
  color: #e0e0e0;
  text-align: center;
}

.work-cities-table-container {
  margin: 20px 0;
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #444;
  border-radius: 4px;
}

.work-cities-table {
  width: 100%;
  border-collapse: collapse;
  color: #e0e0e0;
}

.work-cities-table th,
.work-cities-table td {
  padding: 10px;
  text-align: left;
  border-bottom: 1px solid #444;
}

.work-cities-table th {
  background-color: #3a3a3a;
  font-weight: bold;
}

.work-cities-table tr:hover {
  background-color: #3a3a3a;
}

.work-cities-table input[type="checkbox"] {
  margin: 0;
  transform: scale(1.3);
  cursor: pointer;
}

.work-cities-buttons {
  display: flex;
  justify-content: center;
  gap: 20px;
  margin-top: 20px;
}

/* 选中的城市标签样式 */
.selected-cities-tags {
  display: flex;
  gap: 8px;
  margin:0 0 0 100px;
  min-height: 30px;
  max-height: 60px;
  overflow-x: auto;
  overflow-y: hidden;
  white-space: nowrap;
  padding: 5px 0;
  background-color: #3a3a3a;
  border-radius: 4px;
  box-sizing: border-box;
  width: calc(100% - 100px);
}

.selected-cities-tags::-webkit-scrollbar {
  height: 6px;
}

.selected-cities-tags::-webkit-scrollbar-track {
  background: #333;
  border-radius: 3px;
}

.selected-cities-tags::-webkit-scrollbar-thumb {
  background: #555;
  border-radius: 3px;
}

.selected-cities-tags::-webkit-scrollbar-thumb:hover {
  background: #666;
}

.city-tag {
  display: inline-flex;
  align-items: center;
  background-color: #666;
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 14px;
}

.remove-city-btn {
  background: none;
  border: none;
  color: white;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  padding: 0 4px;
  margin-left: 4px;
  line-height: 1;
}

.remove-city-btn:hover {
  color: #ff4d4f;
}
</style>`,
    '分页组件':`<template>
  <div class="pagination-container">
    <!-- 控制台输出展示板块 -->
    <div class="console-output">
      <h4>控制台输出:</h4>
      <ul v-if="consoleMessages.length > 0">
        <li v-for="(message, index) in consoleMessages" :key="index">{{ message }}</li>
      </ul>
    </div>
    <div class="pagination-info">
      <span>共 {{ total }} 条</span>
      <div class="page-size-selector">
        <select id="pageSizeSelect" v-model="pageSize" @change="handlePageSizeChange">
          <option v-for="size in pageSizeOptions" :key="size" :value="size">
            {{ size }} 条/页
          </option>
        </select>
      </div>
    </div>
    <div class="pagination-controls"  v-if="totalPages > 1">
      <button 
        class="pagination-btn" 
        :disabled="currentPage === 1" 
        @click="prevPage"
        title="上一页"
      >
        &lt;
      </button>
      
      <div class="page-numbers">
        <button
          v-if="showFirstPageButton"
          class="page-number"
          @click="goToPage(1)"
          title="首页"
        >
          1
        </button>
        <span v-if="showFirstEllipsis" class="ellipsis">...</span>
        
        <button
          v-for="page in visiblePages"
          :key="page"
          class="page-number"
          :class="{ active: page === currentPage }"
          @click="goToPage(page)"
        >
          {{ page }}
        </button>
        
        <span v-if="showLastEllipsis" class="ellipsis">...</span>
        <button
          v-if="showLastPageButton"
          class="page-number"
          @click="goToPage(totalPages)"
          title="末页"
        >
          {{ totalPages }}
        </button>
      </div>
      
      <button 
        class="pagination-btn" 
        :disabled="currentPage === totalPages || totalPages === 0" 
        @click="nextPage"
        title="下一页"
      >
        &gt;
      </button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Pagination',
  data() {
    return {
      total: 100,
      pageSize: 10,
      currentPage: 1,
      pageSizeOptions: [5, 10, 20, 50, 100],
      abortController: null,
      consoleMessages: []
    };
  },
  computed: {
    totalPages() {
      return Math.ceil(this.total / this.pageSize) || 1;
    },
    visiblePages() {
      const pages = [];
      const pageCount = 4;
      const halfPageCount = Math.floor(pageCount / 2);
      
      let startPage = Math.max(1, this.currentPage - halfPageCount);
      let endPage = Math.min(this.totalPages, startPage + pageCount - 1);
      
      if (endPage - startPage + 1 < pageCount) {
        startPage = Math.max(1, endPage - pageCount + 1);
      }
      
      for (let i = startPage; i <= endPage; i++) {
        pages.push(i);
      }
      
      return pages;
    },
    showFirstPageButton() {
      return this.visiblePages.length > 0 && this.visiblePages[0] > 1;
    },
    showLastPageButton() {
      return this.visiblePages.length > 0 && this.visiblePages[this.visiblePages.length - 1] < this.totalPages;
    },
    showFirstEllipsis() {
      return this.showFirstPageButton && this.visiblePages[0] > 2;
    },
    showLastEllipsis() {
      return this.showLastPageButton && this.visiblePages[this.visiblePages.length - 1] < this.totalPages - 1;
    }
  },
  methods: {
    prevPage() {
      if (this.currentPage > 1) {
        this.simulatePageChange(this.currentPage - 1);
      }
    },
    nextPage() {
      if (this.currentPage < this.totalPages) {
        this.simulatePageChange(this.currentPage + 1);
      }
    },
    goToPage(page) {
      if (page >= 1 && page <= this.totalPages && page !== this.currentPage) {
        this.addConsoleMessage(\`点击第\${page}页按钮，当前页: \${page}\`);
        this.simulatePageChange(page);
      }
    },
    handlePageSizeChange() {
      this.addConsoleMessage(\`更改页面大小为: \${this.pageSize} 条/页\`);
      this.currentPage = 1;
    },
    beforeDestroy() {
      if (this.abortController) {
        this.abortController.abort();
        this.abortController = null;
      }
    },
    simulatePageChange(page) {
      this.currentPage = page;
      
      if (this.abortController) {
        this.abortController.abort();
        this.abortController = null;
      }
      
      this.abortController = new AbortController();
      const { signal } = this.abortController;
      
      let delay = 0;
      if (page === 2) {
        delay = 3000; 
      } else if (page === 3) {
        delay = 1000;
      }
      
      const timer = setTimeout(() => {
        if (!signal.aborted) {
          let message = "";
          if (page === 2) {
            message = "第2页数据加载完成";
          } else if (page === 3) {
            message = "第3页数据加载完成";
          }
        }
        this.abortController = null;
      }, delay);
      
      signal.addEventListener('abort', () => {
        clearTimeout(timer);
        this.addConsoleMessage(\`第\${page}页请求已被取消\`);
      });
    },
    addConsoleMessage(message) {
      // 添加时间戳
      const timestamp = new Date().toLocaleTimeString();
      this.consoleMessages.unshift(\`[\${timestamp}] \${message}\`);
      
      // 限制消息数量，只保留最近的10条消息
      if (this.consoleMessages.length > 10) {
        this.consoleMessages = this.consoleMessages.slice(0, 10);
      }
    }
  }
};
</script>

<style scoped>
.pagination-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 20px;
  background-color: #333;
  border-radius: 8px;
  color: #e0e0e0;
  font-size: 14px;
  flex-wrap: wrap;
  gap: 10px;
}

.pagination-info {
  display: flex;
  align-items: center;
  gap: 15px;
  white-space: nowrap;
}

.page-size-selector {
  display: flex;
  align-items: center;
  gap: 5px;
}

.page-size-selector label {
  font-size: 14px;
}

.page-size-selector select {
  background-color: #555;
  color: #e0e0e0;
  border: 1px solid #666;
  border-radius: 4px;
  padding: 4px 8px;
  cursor: pointer;
}

.page-size-selector select:focus {
  outline: none;
  border-color: #007bff;
}

.console-output {
  padding: 0;
  margin: 10px 0;
  max-height: 150px;
  overflow-y: auto;
  width: 100%;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE 10+ */
}

.console-output::-webkit-scrollbar {
  width: 0px;
  background: transparent; /* Chrome/Safari/Webkit */
}

.console-output h4 {
  margin: 0 0 8px 0;
  color: #3498db;
  font-size: 14px;
}

.console-output ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

.console-output li {
  padding: 4px 0;
  border-bottom: 1px solid #444;
  font-family: 'Consolas', 'Courier New', monospace;
  font-size: 13px;
  color: #bbeffd;
}

.console-output li:last-child {
  border-bottom: none;
}

.pagination-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.pagination-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #555;
  color: #e0e0e0;
  border: 1px solid #666;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
  flex-shrink: 0;
}

.pagination-btn:hover:not(:disabled) {
  background-color: #666;
}

.pagination-btn:disabled {
  background-color: #444;
  color: #888;
  cursor: not-allowed;
}

.page-numbers {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
}

.page-number {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #555;
  color: #e0e0e0;
  border: 1px solid #666;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
  flex-shrink: 0;
}

.page-number:hover:not(:disabled) {
  background-color: #666;
}

.page-number.active {
  background-color: #007bff;
  border-color: #007bff;
  color: white;
}

.ellipsis {
  padding: 0 5px;
  color: #888;
  flex-shrink: 0;
}

@media (max-width: 768px) {
  .pagination-container {
    flex-direction: column;
    align-items: stretch;
  }
  
  .pagination-controls {
    justify-content: center;
  }
  
  .pagination-info {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
}
</style>`,
    '虚拟滚动':`<template>
  <div class="virtual-scroll-container">
    <div
      class="virtual-scroll-list"
      :style="{ height: containerHeight + 'px' }"
      @scroll="handleScroll"
      ref="scrollContainer"
    >
      <div :style="{ height: totalHeight + 'px' }" class="scroll-placeholder"></div>
      <div
        class="virtual-list"
        :style="{ transform: \`translateY(\${offsetY}px)\` }"
      >
        <div
          v-for="item in visibleItems"
          :key="item.id"
          class="list-item"
          :style="{ height: itemHeight + 'px' }"
        >
          {{ item.content }}
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VirtualScroll',
  data() {
    return {
      items: Array.from({ length: 10000 }, (_, index) => ({
        id: index,
        content: \`项目 \${index + 1}\`
      })),
      containerHeight: 300,
      itemHeight: 50,
      visibleCount: 10,
      scrollTop: 0
    };
  },
  computed: {
    totalHeight() {
      return (this.items && this.items.length) ? this.items.length * this.itemHeight : 0;
    },
    startIndex() {
      return Math.floor(this.scrollTop / this.itemHeight);
    },
    endIndex() {
      return Math.min(this.startIndex + this.visibleCount, this.items.length);
    },
    visibleItems() {
      return this.items.slice(this.startIndex, this.endIndex);
    },
    offsetY() {
      return this.startIndex * this.itemHeight;
    }
  },
  methods: {
    handleScroll(event) {
      this.scrollTop = event.target.scrollTop;
    }
  }
};
</script>

<style scoped>
.virtual-scroll-container {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

.virtual-scroll-list {
  overflow-y: auto;
  position: relative;
  background-color: #333;
  border: 1px solid #444;
  border-radius: 4px;
}

.virtual-scroll-list::-webkit-scrollbar {
  width: 12px;
  background-color: #2d2d2d;
  border-radius: 6px;
}

.virtual-scroll-list::-webkit-scrollbar-track {
  background-color: #2d2d2d;
  border-radius: 6px;
}

.virtual-scroll-list::-webkit-scrollbar-thumb {
  background-color: #555;
  border-radius: 6px;
  border: 2px solid #2d2d2d;
}

.virtual-scroll-list::-webkit-scrollbar-thumb:hover {
  background-color: #777;
}

.virtual-scroll-list::-webkit-scrollbar-corner {
  background-color: #2d2d2d;
}

.scroll-placeholder {
  position: absolute;
  width: 100%;
}

.virtual-list {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

.list-item {
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-bottom: 1px solid #444;
  box-sizing: border-box;
  color: #e0e0e0;
  transition: background-color 0.2s;
}

.list-item:hover {
  background-color: #3a3a3a;
}

.list-item:last-child {
  border-bottom: none;
}
</style>`,
    '地图模块':`<template>
  <div class="map-container">
    <div class="map-header">
      <h2>去过的城市</h2>
    </div>
    <div id="gd-map" class="gd-map"></div>
    <div class="marker-info" v-if="selectedMarker">
      <h3>标记信息</h3>
      <p>位置: {{ selectedMarker.position }}</p>
      <p>描述: {{ selectedMarker.description }}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MapModule',
  data() {
    return {
      map: null,
      markers: [],
      selectedMarker: null,
      animationId: null,
      mapData: {
        center: [115.858198, 28.682892], 
        zoom: 6,
        markers: [
          {
            id: 1,
            position: [115.858198, 28.682892],
            description: '南昌'
          },
          {
            id: 2,
            position: [118.796877, 32.060255],
            description: '南京'
          },
          {
            id: 3,
            position: [118.089475, 24.479298],
            description: '厦门'
          },
          {
            id: 4,
            position: [117.184811, 34.261792],
            description: '徐州'
          }
        ]
      },
      lines: []
    };
  },
  mounted() {
    this.initMap();
  },
  methods: {
    initMap() {
      this.loadAMapScript().then(() => {
        this.map = new AMap.Map('gd-map', {
          zoom: this.mapData.zoom,
          center: this.mapData.center
        });
        
        this.addDefaultMarkers();
      }).catch(error => {
        console.error('地图加载失败:', error);
      });
    },
    
    loadAMapScript() {
      return new Promise((resolve, reject) => {
        if (window.AMap) {
          resolve();
          return;
        }
        
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'https://webapi.amap.com/maps?v=1.4.15&key=YOUR_AMAP_KEY&callback=onAMapLoaded';
        script.onerror = reject;
        
        window.onAMapLoaded = () => {
          resolve();
        };
        
        document.head.appendChild(script);
      });
    },
    
    addDefaultMarkers() {
      this.lines.forEach(line => line.setMap(null));
      this.lines = [];
      
      this.mapData.markers.forEach(markerData => {
        const marker = new AMap.Marker({
          position: markerData.position,
          title: markerData.description
        });
        
        marker.on('click', () => {
          this.selectedMarker = markerData;
          const infoWindow = new AMap.InfoWindow({
            content: \`<div><strong>\${markerData.description}</strong><br>位置: \${markerData.position.join(', ')}</div>\`,
            offset: new AMap.Pixel(0, -30)
          });
          infoWindow.open(this.map, marker.getPosition());
        });
        
        marker.setMap(this.map);
        this.markers.push(marker);
      });
      
      // 添加3D效果的动态连线
      const nanchangPosition = this.mapData.markers[0].position;
      for (let i = 1; i < this.mapData.markers.length; i++) {
        // 创建3D效果的贝塞尔曲线路径
        const targetPosition = this.mapData.markers[i].position;
        const midPoint = [
          (nanchangPosition[0] + targetPosition[0]) / 2,
          (nanchangPosition[1] + targetPosition[1]) / 2
        ];
        
        // 计算控制点，创建弧线效果
        const controlPoint = [
          midPoint[0],
          midPoint[1] + 2 // 增加纬度偏移创建3D弧线效果
        ];
        
        // 创建动态动画效果
        this.createAnimatedLine(nanchangPosition, controlPoint, targetPosition);
      }
    },
    
    // 创建带动画效果的3D连线
    createAnimatedLine(start, control, end) {
      // 创建贝塞尔曲线路径
      const path = [];
      const steps = 100;
      
      for (let i = 0; i <= steps; i++) {
        const t = i / steps;
        // 二次贝塞尔曲线公式
        const x = (1 - t) * (1 - t) * start[0] + 2 * (1 - t) * t * control[0] + t * t * end[0];
        const y = (1 - t) * (1 - t) * start[1] + 2 * (1 - t) * t * control[1] + t * t * end[1];
        path.push([x, y]);
      }
      
      // 创建动态流动效果
      let flowLine = new AMap.Polyline({
        path: path,
        strokeColor: '#3366FF',
        strokeWeight: 2,
        strokeOpacity: 0.8,
        lineCap: 'round',
        zIndex: 50
      });
      
      flowLine.setMap(this.map);
      this.lines.push(flowLine);
      
      // 添加动态流动效果
      this.animateLine(flowLine, path);
    },
    
    // 实现线条流动动画
    animateLine(line, path) {
      let index = 0;
      const total = path.length;
      
      const animate = () => {
        if (!this.map) return;
        
        // 创建流动效果，只显示路径的一部分
        const visiblePath = path.slice(0, index);
        line.setPath(visiblePath);
        
        index += 3; // 控制动画速度
        
        if (index < total + 10) { // 留一些余量确保完整显示
          this.animationId = requestAnimationFrame(animate);
        } else {
          // 动画完成后显示完整路径
          line.setPath(path);
          
          // 添加脉冲效果
          this.pulseLine(line);
        }
      };
      
      this.animationId = requestAnimationFrame(animate);
    },
    
    // 实现线条脉冲效果
    pulseLine(line) {
      let opacity = 0.8;
      let direction = -0.02;
      
      const pulse = () => {
        if (!this.map) return;
        
        opacity += direction;
        if (opacity <= 0.3) {
          direction = 0.02;
        } else if (opacity >= 0.8) {
          direction = -0.02;
        }
        
        line.setOptions({
          strokeOpacity: opacity
        });
        
        // 持续脉冲效果
        this.animationId = requestAnimationFrame(pulse);
      };
      
      this.animationId = requestAnimationFrame(pulse);
    },
    
    addMarker() {
      if (!this.map) return;
      
      const center = this.map.getCenter();
      const marker = new AMap.Marker({
        position: [center.lng, center.lat],
        title: '新标记点'
      });
      
      marker.setMap(this.map);
      this.markers.push(marker);
      
      marker.on('click', () => {
        this.selectedMarker = {
          position: [center.lng, center.lat],
          description: '用户添加的标记点'
        };
      });
    },
    
    clearMarkers() {
      this.markers.forEach(marker => {
        marker.setMap(null);
      });
      this.markers = [];
      this.selectedMarker = null;
      
      // 清理连线动画
      this.lines.forEach(line => {
        line.setMap(null);
      });
      this.lines = [];
    },

  },
  beforeUnmount() {
    // 清理地图实例
    if (this.map) {
      this.map.destroy();
    }
    
    // 清理连线
    this.lines.forEach(line => {
      line.setMap(null);
    });
    this.lines = [];
    
    // 清理动画
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
    }
  }
};
</script>

<style scoped>
.map-container {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  height: 600px;
  display: flex;
  flex-direction: column;
}

.map-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  color: #e0e0e0;
  flex-wrap: wrap;
}

.map-header h2 {
  margin: 0;
}

.controls {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.controls button {
  background: #555;
  color: #fff;
  border: none;
  padding: 8px 15px;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
  white-space: nowrap;
}

.controls button:hover {
  background: #777;
}

.gd-map {
  flex: 1;
  border: 1px solid #444;
  border-radius: 4px;
  overflow: hidden;
}

.marker-info {
  margin-top: 15px;
  padding: 15px;
  background: #444;
  border-radius: 4px;
  color: #e0e0e0;
}

.marker-info h3 {
  margin-top: 0;
}
</style>`,
    '时间线':`<template>
  <div class="horizontal-timeline-container">
    <div class="timeline-header">
      <h3>网站发展历程</h3>
      <p>记录网站从创建到现在的每一个重要时刻</p>
    </div>
    
    <div class="timeline-wrapper">
      <div class="horizontal-timeline">
        <!-- 时间线轴 -->
        <div class="timeline-axis" ref="timelineAxis" :style="{ width: timelineWidth }">
          <!-- 渐变色彩线条 -->
          <div class="timeline-line-gradient"></div>
          
          <!-- 时间点标记 -->
          <div 
            v-for="(event, index) in timelineEvents" 
            :key="index" 
            class="timeline-event-marker"
            :class="{ 'active': activeIndex === index }"
            :style="{ left: \`\${index * 120 + 60}px\` }"
            @mouseenter="setActiveIndex(index)"
            @click="scrollToEvent(index)"
          >
            <div class="event-label">{{ event.date }}</div>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 时间线内容卡片 -->
    <div class="timeline-content-card" v-if="timelineEvents.length > 0">
      <h4 class="timeline-title">{{ timelineEvents[activeIndex].title }}</h4>
      <p class="timeline-description">{{ timelineEvents[activeIndex].description }}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: 'HorizontalTimeline',
  data() {
    return {
      activeIndex: 0,
      timelineEvents: [
        {
          date: "2023-05-21",
          title: "注册域名",
          description: "注册了域名，标志着网站项目的正式启动。"
        },
        {
          date: "2023-06-09",
          title: "网站初版上线",
          description: "网站初版v1.0页面正式上线，实现了基础的展示功能。"
        },
        {
          date: "2023-09-19",
          title: "WebGL背景",
          description: "网站开始使用2D WebGL技术渲染背景，提升了视觉效果。"
        },
        {
          date: "2023-10-08",
          title: "新增音乐模块",
          description: "网站新增音乐模块，为用户提供了更好的听觉体验。"
        },
        {
          date: "2023-10-19",
          title: "新增留言板",
          description: "网站新增留言板模块，增强了用户互动功能。"
        },
        {
          date: "2023-11-07",
          title: "ICP备案",
          description: "网站完成ICP备案，获得了在中国大陆合法运营的资格。"
        },
        {
          date: "2023-11-28",
          title: "全新UI界面",
          description: "网站使用全新UI界面2.0，提升了用户体验和视觉效果。"
        },
        {
          date: "2024-10-18",
          title: "新增旅游足迹",
          description: "网站新增个人旅游足迹模块，展示个人旅行经历。"
        },
        {
          date: "2025-03-05",
          title: "新增绿洲模块",
          description: "网站新增绿洲模块，丰富了网站内容和功能。"
        },
        {
          date: "2025-05-20",
          title: "新增弹幕功能",
          description: "网站新增弹幕功能，增强了用户互动体验。"
        },
        {
          date: "2025-06-12",
          title: "重写技术栈关系图",
          description: "重写了技术栈关系图，更好地展示了项目的技术架构。"
        },
        {
          date: "2025",
          title: "项目施工中",
          description: "项目仍在持续开发中，未来将会有更多功能和改进。"
        }
      ]
    };
  },
  computed: {
    timelineWidth() {
      // 根据事件数量计算时间轴宽度，每个事件占120px，加上左右边距
      return \`\${this.timelineEvents.length * 120 + 100}px\`;
    }
  },
  methods: {
    setActiveIndex(index) {
      this.activeIndex = index;
    },
    // 滚动到指定时间点
    scrollToEvent(index) {
      this.setActiveIndex(index);
      const marker = this.$refs.timelineAxis.children[index + 1]; // +1 because of the gradient line
      if (marker) {
        const container = this.$refs.timelineAxis.parentElement.parentElement;
        const containerWidth = container.offsetWidth;
        const markerPosition = marker.offsetLeft;
        const scrollPosition = markerPosition - containerWidth / 2;
        container.scrollTo({
          left: scrollPosition,
          behavior: 'smooth'
        });
      }
    }
  },
  mounted() {
    // 添加鼠标滚轮事件监听器，用于水平滚动
    const timelineWrapper = this.$el.querySelector('.timeline-wrapper');
    if (timelineWrapper) {
      timelineWrapper.addEventListener('wheel', (e) => {
        if (e.deltaY !== 0) {
          e.preventDefault();
          timelineWrapper.scrollLeft += e.deltaY;
        }
      });
    }
  }
};
</script>

<style scoped>
.horizontal-timeline-container {
  background: #333;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  font-family: 'Arial', sans-serif;
  max-width: 1000px;
  margin: 0 auto;
}

.timeline-content-card {
  background: #2d2d2d;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  min-height: 180px;
  transition: all 0.3s ease;
  margin-top: 20px;
}

.timeline-header {
  text-align: center;
  margin-bottom: 30px;
}

.timeline-header h3 {
  color: #fff;
  font-size: 24px;
  margin-bottom: 10px;
}

.timeline-header p {
  color: #ccc;
  font-size: 14px;
}

.timeline-wrapper {
  overflow-x: auto;
  padding: 10px 0;
}

/* 自定义滚动条样式 */
.timeline-wrapper::-webkit-scrollbar {
  height: 8px;
}

.timeline-wrapper::-webkit-scrollbar-track {
  background: #2d2d2d;
  border-radius: 4px;
}

.timeline-wrapper::-webkit-scrollbar-thumb {
  background: #409eff;
  border-radius: 4px;
}

.timeline-wrapper::-webkit-scrollbar-thumb:hover {
  background: #66b1ff;
}

.horizontal-timeline {
  position: relative;
  width: 100%;
}

/* 时间线轴 */
.timeline-axis {
  position: relative;
  height: 80px;
  margin-bottom: 40px;
  min-width: 100%;
  padding: 0 20px;
}

/* 渐变色彩线条 */
.timeline-line-gradient {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 6px;
  transform: translateY(-50%);
  background: linear-gradient(90deg, #409eff, #67c23a, #e6a23c, #f56c6c, #909399);
  border-radius: 3px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

/* 时间点标记 */
.timeline-event-marker {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 50%;
  cursor: pointer;
  z-index: 2;
  transition: all 0.3s ease;
  box-shadow: 0 0 0 2px #333, 0 0 0 4px #409eff;
}

.timeline-event-marker:hover,
.timeline-event-marker.active {
  transform: translate(-50%, -50%) scale(1.3);
  box-shadow: 0 0 0 2px #333, 0 0 0 6px rgba(64, 158, 255, 0.5);
}

/* 时间点标签 */
.event-label {
  position: absolute;
  top: 25px;
  left: 50%;
  transform: translateX(-50%);
  color: #ccc;
  font-size: 12px;
  white-space: nowrap;
  transition: all 0.3s ease;
}

.timeline-event-marker:hover .event-label,
.timeline-event-marker.active .event-label {
  color: #409eff;
  font-weight: bold;
}

.timeline-title {
  color: #fff;
  font-size: 20px;
  margin: 0 0 15px 0;
}

.timeline-description {
  color: #ccc;
  font-size: 14px;
  line-height: 1.6;
  margin: 0 0 15px 0;
}

.timeline-event-details {
  margin-top: 15px;
}

.event-date {
  color: #409eff;
  font-weight: bold;
  margin-bottom: 10px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .horizontal-timeline-container {
    padding: 15px;
  }
  
  .timeline-header h3 {
    font-size: 20px;
  }
  
  .timeline-axis {
    height: 70px;
  }
  
  .event-label {
    font-size: 10px;
    top: 20px;
  }
  
  .timeline-content-card {
    padding: 15px;
    min-height: 160px;
  }
  
  .timeline-title {
    font-size: 18px;
  }
  
  .timeline-description {
    font-size: 13px;
  }
  
  .event-date {
    font-size: 12px;
  }
}

@media (max-width: 480px) {
  .timeline-axis {
    height: 60px;
  }
  
  .timeline-event-marker {
    width: 14px;
    height: 14px;
  }
  
  .event-label {
    font-size: 9px;
    top: 18px;
  }
  
  .timeline-content-card {
    padding: 12px;
    min-height: 140px;
  }
  
  .timeline-title {
    font-size: 16px;
    margin-bottom: 10px;
  }
  
  .timeline-description {
    font-size: 12px;
    margin-bottom: 10px;
  }
}

/* 大屏幕优化 */
@media (min-width: 1200px) {
  .timeline-axis {
    min-width: 1000px;
  }
}
</style>`
  }
};